/*
 * Copyright(c) 2018-? the original author[Wien(luweien@aliyun.com)] or authors.
 *
 */
package org.doll.it.nora.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.doll.it.base.util.AssertUtils;
import org.doll.it.nora.dao.BlobDAO;
import org.doll.it.nora.dao.CommitDAO;
import org.doll.it.nora.dao.HeadDAO;
import org.doll.it.nora.dao.TreeDAO;
import org.doll.it.nora.domain.BlobDO;
import org.doll.it.nora.domain.CommitDO;
import org.doll.it.nora.domain.HeadDO;
import org.doll.it.nora.domain.TreeDO;
import org.doll.it.sequence.enums.BusinessTypeEnum;
import org.doll.it.sequence.service.DollSequenceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

@Service
public class CommitService {

	@Autowired
	private DollSequenceService sequenceService;

	@Autowired
	private BlobDAO blobDAO;

	@Autowired
	private TreeDAO treeDAO;

	@Autowired
	private CommitDAO commitDAO;

	@Autowired
	private HeadDAO headDAO;

	/**
	 * 添加提交记录
	 */
	public long insert(long originId, String description, Map<Long, Object> kvMap) {
		AssertUtils.notEmpty(kvMap, "Commit data must not be empty: it must contain at least 1 element");
		AssertUtils.verifyId(originId);

		// create blob
		List<BlobDO> blobDOs = new ArrayList<>();
		List<Long> blobIds = new ArrayList<>();
		kvMap.forEach((k, v) -> {
			AssertUtils.verifyId(k);
			long blobId = sequenceService.nextId(BusinessTypeEnum.NORA_BLOB);

			BlobDO blobDO = BlobDO.builder().originId(k).value(v).build();
			blobDO.setId(blobId);
			blobDOs.add(blobDO);
			blobIds.add(blobId);
		});
		blobDAO.insert(blobDOs);

		// create tree
		Assert.notEmpty(blobIds, "Tree blobIds must not be empty: it must contain at least 1 element");
		long treeId = sequenceService.nextId(BusinessTypeEnum.NORA_TREE);
		TreeDO treeDO = TreeDO.builder().blobIds(blobIds).build();
		treeDO.setId(treeId);
		treeDAO.insert(treeDO);

		// query head and create commit
		HeadDO headDO = headDAO.findByOriginId(originId).orElseGet(() -> {
			HeadDO defaultHeadDO = HeadDO.builder().originId(originId).build();
			defaultHeadDO.setId(sequenceService.nextId(BusinessTypeEnum.NORA_HEAD));
			defaultHeadDO.setCommitIds(new ArrayList<>());
			return defaultHeadDO;
		});

		long commitId = sequenceService.nextId(BusinessTypeEnum.NORA_COMMIT);
		CommitDO commitDO = CommitDO.builder().originId(originId).treeId(treeId).parentId(headDO.getCommitId())
				.description(description.trim()).build();
		commitDO.setId(commitId);
		commitDAO.insert(commitDO);

		// save head
		headDO.setCommitId(commitId);
		headDO.getCommitIds().add(0, commitId);
		headDAO.save(headDO);
		return commitId;
	}
}
